import os
import datetime
import csv
import math
import pandas as pd
import numpy as np
from scipy.stats import skew, kurtosis
import asyncio
import selectors
import sys
import warnings
import PySimpleGUI as sg
from tqsdk import TqApi, tafunc
import Spec
import OptionCalc as Oc

warnings.filterwarnings("ignore")
sg.theme('Dark Teal 1')
selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(selector)
asyncio.set_event_loop(loop)
api = TqApi(loop=loop)


def call_chain(sym, name, code, spec, price_recur):
    callcode_list = []
    while True:
        son_recur = getattr(Spec, sym)(name, price_recur)
        cur_pace = son_recur.pace
        callcode_recur = code + spec.opt_codes_adapter[0] + str(price_recur)
        try:
            quote = api.get_quote(callcode_recur)
        except Exception:
            break
        else:
            if np.isnan(quote.bid_price1):
                break
            else:
                callcode_list.append(price_recur)
                price_recur += cur_pace
    return callcode_list


def put_chain(sym, name, code, spec, price_recur):
    putcode_list = []
    while True:
        son_recur = getattr(Spec, sym)(name, price_recur)
        cur_pace = son_recur.pace
        putcode_recur = code + spec.opt_codes_adapter[1] + str(price_recur)
        try:
            quote = api.get_quote(putcode_recur)
        except Exception:
            break
        else:
            if np.isnan(quote.bid_price1):
                break
            else:
                putcode_list.append(price_recur)
                price_recur -= cur_pace
    return putcode_list


def validdict(nlist, length=252, r=0.036):
    # 返回一个字典，包含所有未到期的合约信息，字典格式：标的代码：标的数据， 平值call数据，平值put数据
    valid_dic = {}
    for name in nlist:
        spec = Spec.Spec(name)
        sym = spec.symbol
        underlying_codes = spec.underlying_codes
        current_date = datetime.datetime.now().date()
        for code in underlying_codes:
            underlying_quote = api.get_quote(code)
            price = underlying_quote.last_price
            son = getattr(Spec, sym)(name, price)
            try:
                atm = int(son.atm)
            except ValueError:
                continue
            else:
                opt_call = code + spec.opt_codes_adapter[0] + str(atm)
                opt_put = code + spec.opt_codes_adapter[1] + str(atm)
                call_quote, put_quote = api.get_quote(opt_call), api.get_quote(opt_put)
                opt_expire_date = tafunc.time_to_datetime(call_quote.expire_datetime).date()
                valid_days = (opt_expire_date - current_date).days
                if valid_days < 0:
                    pass
                else:
                    valid_dic[code] = [valid_days, name, length, r]
    return valid_dic


def find_min_cpspread(name, strike_chain, underlying_code):
    # 找出期权链中，C P 中间价差值最小的那一项，返回那一项的差值，以及那一项的行权价
    spec = Spec.Spec(name)
    bar = 100000
    for code_core in strike_chain:
        opt_call_code = underlying_code + spec.opt_codes_adapter[0] + str(code_core)
        call_p = (api.get_quote(opt_call_code).bid_price1 + api.get_quote(opt_call_code).ask_price1) / 2
        opt_put_code = underlying_code + spec.opt_codes_adapter[1] + str(code_core)
        put_p = (api.get_quote(opt_put_code).bid_price1 + api.get_quote(opt_put_code).ask_price1) / 2
        if np.isnan(call_p) or np.isnan(put_p):
            continue
        else:
            delta_p = call_p - put_p
            if abs(delta_p) >= bar:
                pass
            else:
                bar = abs(delta_p)
                min_delta = delta_p
                min_core = code_core
    return min_delta, min_core


def modelfree_calc(name, r, tte, underlying_code, strike_chain):
    # 计算单个合约的sigma与S，方法参考CBOE VIX白皮书
    # sigma即为单个合约的波动率指数vix，S即为单个合约的无模型偏度
    # 通过白皮书上的拟合方法，可以把近月与次月的结果拟合为总体的VIX指数与总体的Skew指数，但此处不涉及拟合
    spec = Spec.Spec(name)
    sym = spec.symbol
    t = tte / 365
    min_delta, min_core = find_min_cpspread(name, strike_chain, underlying_code)
    F = min_core + math.exp(r * t) * min_delta
    son = getattr(Spec, sym)(name, F)
    atm = int(son.atm)
    data_dict = {}
    put_list = [x for x in strike_chain if x < atm]
    p_count = 0
    for core in sorted(put_list, reverse=True):
        code = underlying_code + spec.opt_codes_adapter[1] + str(core)
        p_bid = api.get_quote(code).bid_price1
        p_ask = api.get_quote(code).ask_price1
        if np.isnan(p_bid):
            p_count += 1
            if p_count >= 2:
                break
            else:
                pass
        else:
            put_midpoint = (p_bid + p_ask) / 2
        data_dict[core] = put_midpoint
    call_list = [x for x in strike_chain if x > atm]
    c_count = 0
    for core in sorted(call_list):
        code = underlying_code + spec.opt_codes_adapter[0] + str(core)
        c_bid = api.get_quote(code).bid_price1
        c_ask = api.get_quote(code).ask_price1
        if np.isnan(c_bid):
            c_count += 1
            if c_count >= 2:
                break
            else:
                pass
        else:
            call_midpoint = (c_bid + c_ask) / 2
        data_dict[core] = call_midpoint
    atm_call = underlying_code + spec.opt_codes_adapter[0] + str(atm)
    atm_put = underlying_code + spec.opt_codes_adapter[1] + str(atm)
    atm_call_mid = (api.get_quote(atm_call).ask_price1 + api.get_quote(atm_call).bid_price1) / 2
    atm_put_mid = (api.get_quote(atm_put).ask_price1 + api.get_quote(atm_put).bid_price1) / 2
    atm_pc_average = (atm_call_mid + atm_put_mid) / 2
    data_dict[atm] = atm_pc_average
    corelist = sorted(list(data_dict.keys()))
    k0 = sorted(put_list)[-1]
    kslist = []
    pf1, pf2, pf3 = [], [], []
    for i, k in enumerate(corelist):
        if i == 0:
            deltaki = corelist[i + 1] - k
        elif i == len(corelist) - 1:
            deltaki = k - corelist[i - 1]
        else:
            deltaki = (corelist[i + 1] - corelist[i - 1]) / 2
        # 这部分是循环内的vix部分的运算
        ks = (deltaki / (k ** 2)) * math.exp(r * t) * data_dict[k]
        kslist.append(ks)
        # 这部分是循环内的skew部分的运算
        pfs1 = (deltaki * data_dict[k]) / (k ** 2)
        pfs2 = (2 / (k ** 2)) * data_dict[k] * deltaki * (1 - math.log(k / F))
        pfs3 = (3 / (k ** 2)) * ((2 * math.log(k / F)) - (data_dict[k] * deltaki * ((math.log(k / F)) ** 2)))
        pf1.append(pfs1)
        pf2.append(pfs2)
        pf3.append(pfs3)
    # 这部分是循环外的VIX部分的运算
    sigmasq = (2 / t) * sum(kslist) - (1 / t) * ((F / k0) - 1) ** 2
    sigma = math.sqrt(sigmasq)
    # 这部分是循环外的SKEW部分的运算
    ep1 = -(1 + math.log(F / k0) - (F / k0))
    ep2 = 2 * math.log(k0 / F) * ((F / k0) - 1) + 0.5 * ((math.log(k0 / F)) ** 2)
    ep3 = 3 * ((math.log(k0 / F)) ** 2) * (((1 / 3) * math.log(k0 / F)) - 1 + (F / k0))
    p1 = math.exp(r * t) * (-sum(pf1)) + ep1
    p2 = math.exp(r * t) * sum(pf2) + ep2
    p3 = math.exp(r * t) * sum(pf3) + ep3
    S = (p3 - (3 * p1 * p2) + (2 * (p1 ** 3))) / ((p2 - (p1 ** 2)) ** (3 / 2))
    S = 000 if type(S) == complex else S
    return sigma, S


def iv_calc(opt_type, tte, spot_price, strike_price, r, call_price, put_price):
    if opt_type == 'EU':
        iv_call = Oc.bsiv_call(spot_price, strike_price, tte, r, call_price, 0)
        iv_put = Oc.bsiv_put(spot_price, strike_price, tte, r, put_price, 0)
    else:
        iv_call = Oc.bawiv_call(spot_price, strike_price, tte, r, call_price, 0)
        iv_put = Oc.bawiv_put(spot_price, strike_price, tte, r, put_price, 0)
    result = (iv_call + iv_put) / 2
    return result


def simple_skew_calc(name, opt_type, underlying_code, tte, spot_price, opt_chain, atm, r):
    # 用简单估算法计算CSkew 和PSkew （IV0.25delta - IV0.5delta)
    spec = Spec.Spec(name)
    atm_index = opt_chain.index(atm)
    calls = opt_chain[(atm_index - 2):]
    cbar25, cbar50 = 1, 1
    for nc in calls:
        code = underlying_code + spec.opt_codes_adapter[0] + str(nc)
        quote = (api.get_quote(code).ask_price1 + api.get_quote(code).bid_price1) / 2
        if opt_type == 'EU':
            iv_call = Oc.bsiv_call(spot_price, nc, tte, r, quote, 0)
        else:
            iv_call = Oc.bawiv_call(spot_price, nc, tte, r, quote, 0)
        delta = Oc.bsdelta_call(spot_price, nc, tte, iv_call, r, 0)
        if abs(delta - 0.5) >= cbar50:
            pass
        else:
            cbar50 = abs(delta - 0.5)
            civ50 = iv_call
        if abs(delta - 0.25) >= cbar25:
            if cbar25 == 1:
                pass
            else:
                break
        else:
            cbar25 = abs(delta - 0.25)
            civ25 = iv_call
            strike25_call = nc
    cskew = round((civ25 - civ50) * 100, 2)

    puts = opt_chain[:(atm_index + 2)]
    puts.reverse()
    pbar25, pbar50 = 1, 1
    for np in puts:
        code = underlying_code + spec.opt_codes_adapter[1] + str(np)
        quote = (api.get_quote(code).ask_price1 + api.get_quote(code).bid_price1) / 2
        if opt_type == 'EU':
            iv_put = Oc.bsiv_put(spot_price, np, tte, r, quote, 0)
        else:
            iv_put = Oc.bawiv_put(spot_price, np, tte, r, quote, 0)
        delta = Oc.bsdelta_put(spot_price, np, tte, iv_put, r, 0)
        if abs(delta + 0.5) >= pbar50:
            pass
        else:
            pbar50 = abs(delta + 0.5)
            piv50 = iv_put
        if abs(delta + 0.25) >= pbar25:
            if pbar25 == 1:
                pass
            else:
                break
        else:
            pbar25 = abs(delta + 0.25)
            piv25 = iv_put
            strike25_put = np
    pskew = round((piv25 - piv50) * 100, 2)
    return cskew, pskew, strike25_call, strike25_put


def realtime_1(n, v):
    # 计算各个指标的函数
    # 注：IV相关的函数用的是自然日，一方面是因为IV隐含非交易日的theta decay带来的影响，另一方面是因为到期时间用的是自然日时间。
    # HV用的是交易日，因为HV计算时的样本都来自于交易日。
    underlying_code = n
    valid_days = v[0]
    name = v[1]
    length = v[2]
    r = v[3]
    spec = Spec.Spec(name)
    sym = spec.symbol
    tte = valid_days + 1
    underlying_price = api.get_quote(underlying_code).last_price
    preclose = api.get_quote(underlying_code).pre_close
    pctg_chg = str(round(((underlying_price - preclose) / preclose) * 100, 2)) + "%"
    son = getattr(Spec, sym)(name, underlying_price)
    try:
        atm = int(son.atm)
    except ValueError:
        msg = ['null'] * 9
        hv = np.nan
    else:
        call_price_recur = atm + son.pace
        put_price_recur = atm - son.pace
        callstrikes = call_chain(sym, name, underlying_code, spec, call_price_recur)
        putstrikes = put_chain(sym, name, underlying_code, spec, put_price_recur)
        klines = api.get_kline_serial(underlying_code, 86400)
        closelist = klines.iloc[-21:].close.dropna().tolist()
        # HV计算(20交易日)
        logcloselist = np.log(np.array(closelist)).tolist()
        logdelta = np.diff(np.array(logcloselist))
        std_logdelta = np.std(logdelta)
        hv = round(float(100 * std_logdelta * math.sqrt(length)), 2)  # HV计算使用的是252日，因为使用的是20交易日的数据。
        # 历史对数收益率分布偏度计算
        hist_skew = float(skew(logdelta))
        hsk = round((100 - 10 * hist_skew), 2)
        # 历史对数收益率分布峰度计算
        hkr = round(float(kurtosis(logdelta)), 2)
        # 单个标的合约的VIX指数与偏度Skew计算
        strike_chain = sorted(callstrikes + [atm] + putstrikes)
        try:
            sigma, S = modelfree_calc(name, r, tte, underlying_code, strike_chain)
        except Exception:
            vix = 99999
            skw = 99999
        else:
            vix = round((100 * sigma), 2)
            skw = round((100 - (10 * S)), 2)
        msg = [underlying_code, underlying_price, pctg_chg, vix, hv, skw, hsk, hkr, valid_days]

    return msg, hv


def realtime_2(n, v, dataf, hv):
    # 计算各个指标的函数
    # 注：IV相关的函数用的是自然日，一方面是因为IV隐含非交易日的theta decay带来的影响，另一方面是因为到期时间用的是自然日时间。
    # HV用的是交易日，因为HV计算时的样本都来自于交易日。
    underlying_code = n
    valid_days = v[0]
    name = v[1]
    r = v[3]
    spec = Spec.Spec(name)
    opt_type = spec.type
    sym = spec.symbol
    tte = valid_days + 1
    t = tte / 365
    underlying_price = api.get_quote(underlying_code).last_price
    son = getattr(Spec, sym)(name, underlying_price)
    try:
        atm = int(son.atm)
    except ValueError:
        msg = ['null'] * 6
    else:
        atm_call = underlying_code + spec.opt_codes_adapter[0] + str(atm)
        atm_put = underlying_code + spec.opt_codes_adapter[1] + str(atm)
        call_price_recur = atm + son.pace
        put_price_recur = atm - son.pace
        callstrikes = call_chain(sym, name, underlying_code, spec, call_price_recur)
        putstrikes = put_chain(sym, name, underlying_code, spec, put_price_recur)
        # 即期价格对应期权到期的远期价格
        f_underlying_price = underlying_price * math.exp(r * t)
        atm_call_price = (api.get_quote(atm_call).bid_price1 + api.get_quote(atm_call).ask_price1) / 2
        atm_put_price = (api.get_quote(atm_put).bid_price1 + api.get_quote(atm_put).ask_price1) / 2

        strike_chain = sorted(callstrikes + [atm] + putstrikes)
        # 简单方法计算cskew与pskew, 以及0.25delta对应的call和put行权价
        cskew, pskew, strike25_c, strike25_p = simple_skew_calc(name, opt_type, underlying_code, tte,
                                                                underlying_price, strike_chain, atm, r)
        # 计算合约的IV，目前采用平值C-P的IV求均值的方法近似
        iv = iv_calc(opt_type, tte, underlying_price, atm, r, atm_call_price, atm_put_price)
        iv_result = round((100 * iv), 2)
        if iv_result > 100 or iv_result > hv * 3:
            calc_v = hv / 100
        else:
            calc_v = iv
        # Implied daily volitility，年时间也改为计算IV所对应的365天，因为IV隐含了非交易日的变化
        daily_range = round(iv_result / math.sqrt(365), 2)

        # 使用IV来计算1sd的范围，取消之前使用的估计IV推导sd，年时间也改为计算IV所对应的365天，因为IV隐含了非交易日的变化
        sd_pctg_tte = round(((iv_result / 100) * math.sqrt(tte / 365)), 2)
        max_1sd = round(f_underlying_price * (1 + sd_pctg_tte), 2)
        min_1sd = round(f_underlying_price * (1 - sd_pctg_tte), 2)

        msg = [max_1sd, min_1sd, iv_result, daily_range, cskew, pskew]

        # 使用自设的价格区间与上涨概率等数据（来源于watchlist.csv）计算出可以部署的头寸规模
        try:
            dflist = dataf[n].to_list()
        except KeyError:
            ls = '000'
            pos = '000'
            target_strike = '000'
            current_opt_price = '000'
            target_price = '000'
            stopline = '000'
        else:
            if len(dflist) == 0:
                ls = '000'
                pos = '000'
                target_strike = '000'
                current_opt_price = '000'
                target_price = '000'
                stopline = '000'
            else:
                ls = dflist[0]
                upper = float(dflist[1])
                lower = float(dflist[2])
                prob = float(dflist[3])
                if ls == 'long':
                    if strike25_c <= upper:
                        target_price = Oc.bawprice_call(
                            strike25_c, strike25_c,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_call(strike25_c,
                                                                                      strike25_c, tte, calc_v, r, q=0)
                        stopline = Oc.bawprice_call(
                            lower, strike25_c,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_call(lower,
                                                                                      strike25_c, tte, calc_v, r, q=0)
                        current_opt_price = api.get_quote(underlying_code + spec.opt_codes_adapter[0] +
                                                          str(strike25_c)).ask_price1
                        target_strike = str(strike25_c)
                    else:
                        son = getattr(Spec, sym)(name, upper)
                        atm = int(son.atm)
                        target_price = Oc.bawprice_call(
                            upper, atm,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_call(upper,
                                                                                      atm, tte, calc_v, r, q=0)
                        stopline = Oc.bawprice_call(
                            lower, atm,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_call(lower,
                                                                                      atm, tte, calc_v, r, q=0)
                        current_opt_price = api.get_quote(underlying_code + spec.opt_codes_adapter[0] +
                                                          str(atm)).ask_price1
                        target_strike = str(atm)
                else:
                    if strike25_p >= lower:
                        target_price = Oc.bawprice_put(
                            strike25_p, strike25_p,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_put(strike25_p,
                                                                                     strike25_p, tte, calc_v, r, q=0)
                        stopline = Oc.bawprice_put(
                            upper, strike25_p,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_put(upper,
                                                                                     strike25_p, tte, calc_v, r, q=0)
                        current_opt_price = api.get_quote(underlying_code + spec.opt_codes_adapter[1] +
                                                          str(strike25_p)).ask_price1
                        target_strike = str(strike25_p)
                    else:
                        son = getattr(Spec, sym)(name, lower)
                        atm = int(son.atm)
                        target_price = Oc.bawprice_put(
                            lower, atm,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_put(lower,
                                                                                     atm, tte, calc_v, r, q=0)
                        stopline = Oc.bawprice_put(
                            upper, atm,
                            tte, calc_v, r, q=0) if type == 'AM' else Oc.bsprice_put(upper,
                                                                                     atm, tte, calc_v, r, q=0)
                        current_opt_price = api.get_quote(underlying_code + spec.opt_codes_adapter[1] +
                                                          str(atm)).ask_price1
                        target_strike = str(atm)

                profit = target_price - current_opt_price
                loss = current_opt_price - stopline
                plratio = profit / loss
                if underlying_price >= upper:
                    pos = '+++'
                elif underlying_price <= lower:
                    pos = '---'
                else:
                    if loss != 0 and plratio >= 3:
                        pos = (plratio * prob - (1 - prob)) / (plratio * 2) if ls == 'long' else \
                            (plratio * (1 - prob) - (1 - (1 - prob))) / (plratio * 2)
                        pos = round((100 * pos), 2)
                    elif loss != 0 and plratio < 3:
                        pos = 0
                    else:
                        pos = 'xxx'
                target_price = round(target_price, 2)
                stopline = round(stopline, 2)

        msg = msg + [ls, target_strike, current_opt_price, target_price, stopline, pos]

    return msg


async def gui_task():
    global sourcedict_shfe, sourcedict_czce, sourcedict_dce, df
    layout_shfe = [[sg.Text('Contract_s', size=(12, 1)),
                    sg.Text('Spot_s', size=(8, 1)),
                    sg.Text('Chg%_s', size=(6, 1)),
                    sg.Text('+1SD_s', size=(8, 1)),
                    sg.Text('-1SD_s', size=(8, 1)),
                    sg.Text('VIX_s', size=(6, 1)),
                    sg.Text('IV_s', size=(6, 1)),
                    sg.Text('HV_s', size=(6, 1)),
                    sg.Text('daily%_s', size=(6, 1)),
                    sg.Text('SKEW_s', size=(6, 1)),
                    sg.Text('cSKEW_s', size=(6, 1)),
                    sg.Text('pSKEW_s', size=(6, 1)),
                    sg.Text('HSkew_s', size=(6, 1)),
                    sg.Text('HKurt_s', size=(6, 1)),
                    sg.Text('Due_s', size=(6, 1)),
                    sg.Text('L/S_s', size=(4, 1)),
                    sg.Text('Strike_s', size=(6, 1)),
                    sg.Text('Opt_p_s', size=(6, 1)),
                    sg.Text('Target_s', size=(6, 1)),
                    sg.Text('Stop_s', size=(6, 1)),
                    sg.Text('Pos%_s', size=(4, 1))]]
    layout_dce = [[sg.Text('Contract_d', size=(12, 1)),
                   sg.Text('Spot_d', size=(8, 1)),
                   sg.Text('Chg%_d', size=(6, 1)),
                   sg.Text('+1SD_d', size=(8, 1)),
                   sg.Text('-1SD_d', size=(8, 1)),
                   sg.Text('VIX_d', size=(6, 1)),
                   sg.Text('IV_d', size=(6, 1)),
                   sg.Text('HV_d', size=(6, 1)),
                   sg.Text('daily%_d', size=(6, 1)),
                   sg.Text('SKEW_d', size=(6, 1)),
                   sg.Text('cSKEW_d', size=(6, 1)),
                   sg.Text('pSKEW_d', size=(6, 1)),
                   sg.Text('HSkew_d', size=(6, 1)),
                   sg.Text('HKurt_d', size=(6, 1)),
                   sg.Text('Due_d', size=(6, 1)),
                   sg.Text('L/S_d', size=(4, 1)),
                   sg.Text('Strike_d', size=(6, 1)),
                   sg.Text('Opt_p_d', size=(6, 1)),
                   sg.Text('Target_d', size=(6, 1)),
                   sg.Text('Stop_d', size=(6, 1)),
                   sg.Text('Pos%_d', size=(4, 1))]]
    layout_czce = [[sg.Text('Contract_c', size=(12, 1)),
                    sg.Text('Spot_c', size=(8, 1)),
                    sg.Text('Chg%_c', size=(6, 1)),
                    sg.Text('+1SD_c', size=(8, 1)),
                    sg.Text('-1SD_c', size=(8, 1)),
                    sg.Text('VIX_c', size=(6, 1)),
                    sg.Text('IV_c', size=(6, 1)),
                    sg.Text('HV_c', size=(6, 1)),
                    sg.Text('daily%_c', size=(6, 1)),
                    sg.Text('SKEW_c', size=(6, 1)),
                    sg.Text('cSKEW_c', size=(6, 1)),
                    sg.Text('pSKEW_c', size=(6, 1)),
                    sg.Text('HSkew_c', size=(6, 1)),
                    sg.Text('HKurt_c', size=(6, 1)),
                    sg.Text('Due_c', size=(6, 1)),
                    sg.Text('L/S_c', size=(4, 1)),
                    sg.Text('Strike_c', size=(6, 1)),
                    sg.Text('Opt_p_c', size=(6, 1)),
                    sg.Text('Target_c', size=(6, 1)),
                    sg.Text('Stop_c', size=(6, 1)),
                    sg.Text('Pos%_c', size=(4, 1))]]

    def add_layout(layout, sourcedic):
        for k, v in sourcedic.items():
            line = [sg.Text('000000', key='code{}'.format(k), size=(12, 1)),
                    sg.Text('000000', key='spot{}'.format(k), size=(8, 1)),
                    sg.Text('000000', key='Chg%{}'.format(k), size=(6, 1)),
                    sg.Text('000000', key='+1sd{}'.format(k), size=(8, 1)),
                    sg.Text('000000', key='-1sd{}'.format(k), size=(8, 1)),
                    sg.Text('00000', key='vix{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='iv{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='hv{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='d_range{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='skew{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='cskew{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='pskew{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='hsk{}'.format(k), size=(6, 1)),
                    sg.Text('00000', key='hkr{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='due{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='ls{}'.format(k), size=(4, 1)),
                    sg.Text('000', key='strike{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='opt_p{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='target{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='stop{}'.format(k), size=(6, 1)),
                    sg.Text('000', key='pos{}'.format(k), size=(4, 1))]
            layout.append(line)
        return layout

    layout_shfe = add_layout(layout_shfe, sourcedict_shfe)
    layout_dce = add_layout(layout_dce, sourcedict_dce)
    layout_czce = add_layout(layout_czce, sourcedict_czce)
    gen_layout = [
        [sg.TabGroup([[sg.Tab('SHFE', layout_shfe), sg.Tab('DCE', layout_dce), sg.Tab('CZCE', layout_czce)]])]
    ]

    io_buttons = [sg.Button('Read Watchlist'), sg.Button('Open Watchlist'),
                  sg.Button('Build Watchlist'), sg.Button('Exit')]
    gen_layout.append(io_buttons)
    express_msg1 = [sg.Text("* H: Indicators on historical log returns S>100 = Negtive Skew, S<100 = Positive Skew, "
                            "Historical Period = 20 trade days")]
    express_msg2 = [sg.Text("* C/P Skew: Indicators of skew on call/put respectively. "
                            "A greater reading indicates more buying power on this direction.")]
    gen_layout.append(express_msg1)
    gen_layout.append(express_msg2)
    window = sg.Window('Option Wizard II | MuZe Capital.', gen_layout)

    def window_updater(sourcedict):
        for n, v in sourcedict.items():
            window.Element('code{}'.format(n)).Update(v[0])
            window.Element('spot{}'.format(n)).Update(v[1])
            window.Element('Chg%{}'.format(n)).Update(v[2])
            window.Element('vix{}'.format(n)).Update(v[3])
            window.Element('hv{}'.format(n)).Update(v[4])
            window.Element('skew{}'.format(n)).Update(v[5])
            window.Element('hsk{}'.format(n)).Update(v[6])
            window.Element('hkr{}'.format(n)).Update(v[7])
            window.Element('due{}'.format(n)).Update(v[8])

            window.Element('+1sd{}'.format(n)).Update(v[9])
            window.Element('-1sd{}'.format(n)).Update(v[10])
            window.Element('iv{}'.format(n)).Update(v[11])
            window.Element('d_range{}'.format(n)).Update(v[12])
            window.Element('cskew{}'.format(n)).Update(v[13])
            window.Element('pskew{}'.format(n)).Update(v[14])
            window.Element('ls{}'.format(n)).Update(v[15])
            window.Element('strike{}'.format(n)).Update(v[16])
            window.Element('opt_p{}'.format(n)).Update(v[17])
            window.Element('target{}'.format(n)).Update(v[18])
            window.Element('stop{}'.format(n)).Update(v[19])
            window.Element('pos{}'.format(n)).Update(v[20])

    while True:
        event, values = window.Read(timeout=0)
        if event is None or event == 'Exit':
            sys.exit(0)
        elif event == 'Build Watchlist':
            with open('..//watchlist//watchlist.csv', 'w+') as f:
                iwriter = csv.writer(f)
                iwriter.writerow(
                    list(valid_dict_shfe.keys()) + list(valid_dict_dce.keys()) + list(valid_dict_czce.keys()))
            os.system('start ..//watchlist//watchlist.csv')
        elif event == 'Open Watchlist':
            os.system('start ..//watchlist//watchlist.csv')
        elif event == 'Read Watchlist':
            df = pd.read_csv('..//watchlist//watchlist.csv')

        sourcedict_shfe, sourcedict_dce, sourcedict_czce = {}, {}, {}
        for n, v in valid_dict_shfe.items():
            window.Refresh()
            msg1, hv1 = realtime_1(n, v)
            window.Refresh()
            msg2 = realtime_2(n, v, df, hv1)
            window.Refresh()
            sourcedict_shfe[n] = msg1 + msg2
        for n, v in valid_dict_dce.items():
            window.Refresh()
            msg1, hv2 = realtime_1(n, v)
            window.Refresh()
            msg2 = realtime_2(n, v, df, hv2)
            window.Refresh()
            sourcedict_dce[n] = msg1 + msg2
        for n, v in valid_dict_czce.items():
            window.Refresh()
            msg1, hv3 = realtime_1(n, v)
            window.Refresh()
            msg2 = realtime_2(n, v, df, hv3)
            window.Refresh()
            sourcedict_czce[n] = msg1 + msg2
        window_updater(sourcedict_shfe)
        window_updater(sourcedict_dce)
        window_updater(sourcedict_czce)
        await asyncio.sleep(1)


if __name__ == "__main__":
    shfes = Spec.Spec.namelist_shfe
    dces = Spec.Spec.namelist_dce
    czces = Spec.Spec.namelist_czce
    valid_dict_shfe = validdict(shfes)
    valid_dict_dce = validdict(dces)
    valid_dict_czce = validdict(czces)
    try:
        df = pd.read_csv('..//watchlist//watchlist.csv')
    except FileNotFoundError:
        with open('..//watchlist//watchlist.csv', 'w+') as file:
            writer = csv.writer(file)
            writer.writerow(list(valid_dict_shfe.keys()) + list(valid_dict_dce.keys()) + list(valid_dict_czce.keys()))
        df = pd.read_csv('..//watchlist//watchlist.csv')
    else:
        pass
    sourcedict_shfe = {}
    for ii in list(valid_dict_shfe.keys()):
        sourcedict_shfe[ii] = ['...'] * 21
    sourcedict_dce = {}
    for ii in list(valid_dict_dce.keys()):
        sourcedict_dce[ii] = ['...'] * 21
    sourcedict_czce = {}
    for ii in list(valid_dict_czce.keys()):
        sourcedict_czce[ii] = ['...'] * 21
    api.create_task(gui_task())
    while True:
        api.wait_update()
